package chain

import (
	"context"
	"fmt"
	"github.com/simplechain-org/go-simplechain/common"
	"github.com/simplechain-org/go-simplechain/p2p"
	"math/big"
	"strconv"
	"strings"
)

type Node struct {
	Id       int    `json:"id"`
	Enode    string `json:"enode"`
	EnodeId  string `json:"enode_id"`
	NodeName string `json:"node_name"`
	Ip       string `json:"ip"`
	Port     string `json:"port"`
	Address  string `json:"address"`
	NodeRole string `json:"nodeRole"`
	IsSelf   bool   `json:"isSelf"`
	IsMiner  bool   `json:"isMiner"`
	Status   string `json:"status"`
}

// UpdateNodeName 更新节点名称
func (c *Client) UpdateNodeName(ctx context.Context, nodeId string, nodeName string, from string) (string, error) {
	var result string
	err := c.rpcClient.CallContext(ctx, &result, "permission_updateNodeName", nodeId, nodeName, from)
	if err != nil {
		return "", err
	}
	return result, nil
}
func (c *Client) getNodeInfo(from, enodeId string) (*Node, error) {
	if enodeId == "" {
		return nil, nil
	}
	var ret string
	err := c.rpcClient.Call(&ret, "permission_getNodeInfo", enodeId, from)
	if err != nil {
		return nil, err
	}
	tmp := strings.Split(ret, ",")
	if len(tmp) == 0 {
		return nil, nil
	}
	port := strings.Split(tmp[2], "?")[0]
	eNode := tmp[0] + "@" + tmp[1] + ":" + tmp[2]
	node := &Node{
		Id:       0,
		Enode:    tmp[0],
		EnodeId:  eNode,
		NodeName: tmp[3],
		Ip:       tmp[1],
		Port:     port,
		Address:  tmp[4],
		IsMiner:  c.minerStatus(tmp[6]),
	}
	if tmp[5] == fmt.Sprintf("%v", NormalNodeRole) { //normal role node
		node.NodeRole = NormalNode
	}
	if tmp[5] == fmt.Sprintf("%v", AdminNodeRole) { //admin role node
		if tmp[6] == fmt.Sprintf("%v", AlwaysMiner) {
			node.NodeRole = LeaderNode
		} else {
			node.NodeRole = AdminNode
		}
	}
	return node, nil
}
func (c *Client) getAllNodeKey(from string, role int) ([]string, error) {
	var ret string
	err := c.rpcClient.Call(&ret, "permission_getAllNode", big.NewInt(int64(role)), from)
	if err != nil {
		return nil, err
	}
	if len(ret) == 0 {
		return nil, nil
	}
	return strings.Split(ret, ","), nil
}
func (c *Client) getNodesOnChain(nodeType, from, selfNodeId string) ([]*Node, error) {
	role, _ := strconv.Atoi(nodeType)
	keys, err := c.getAllNodeKey(from, role)
	if err != nil {
		return nil, err
	}
	var ret []*Node
	if keys != nil {
		for _, value := range keys {
			node, err := c.getNodeInfo(from, value)
			if err != nil {
				continue
			}
			if node != nil {
				if node.Enode == selfNodeId {
					node.IsSelf = true
				}
				ret = append(ret, node)
			}
		}
	}
	return ret, nil
}

func (c *Client) GetNodesOnChain(from, selfNode string) ([]*Node, error) {
	var ret []*Node
	if !common.IsHexAddress(from) {
		return nil, fmt.Errorf("addr is not HexAddress")
	}
	//todo check error
	// get normal node
	ret1, _ := c.getNodesOnChain(fmt.Sprintf("%v", NormalNodeRole), from, selfNode)
	// get admin node
	ret2, _ := c.getNodesOnChain(fmt.Sprintf("%v", AdminNodeRole), from, selfNode)
	ret = append(ret, ret1...)
	ret = append(ret, ret2...)
	ret, err := c.verifyStatus(ret, selfNode)
	if err != nil {
		return ret, err
	}
	return ret, nil
}
func (c *Client) GetNodesOnChainOnline() ([]*Node, int, error) {
	account, err := c.GetAccounts()
	if err != nil {
		return nil, -1, err
	}
	tmp, err := c.getLocalEnode()
	if err != nil {
		return nil, -1, err
	} else if len(tmp) == 0 {
		return nil, -1, fmt.Errorf("get local enode failed")
	}
	selfNode := tmp[0]
	role, err := c.getNodeRole(account[0], selfNode)
	if err != nil {
		return nil, -1, err
	}
	ret, err := c.GetNodesOnChain(account[0], selfNode)
	if err != nil {
		return nil, -1, err
	}
	for index, _ := range ret {
		ret[index].Id = index
	}
	return ret, int(role.Int64()), nil
}

func (c *Client) getLocalEnode() ([]string, error) {
	var ret string
	err := c.rpcClient.Call(&ret, "permission_getLocalEnode")
	if err != nil {
		return nil, err
	}
	return strings.Split(ret, ","), nil
}

//获取节点的角色
func (c *Client) getNodeRole(from, enodeId string) (*big.Int, error) {
	var ret *big.Int
	err := c.rpcClient.Call(&ret, "permission_getNodeMap", enodeId, from)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
func (c *Client) GetAllNodes(roleType int, from string) ([]string, error) {
	var ret interface{}
	if !common.IsHexAddress(from) {
		return nil, fmt.Errorf("addr is not HexAddress")
	}
	err := c.rpcClient.Call(&ret, "permission_getAllNode", float64(roleType), common.HexToAddress(from))
	if err != nil {
		return nil, err
	}
	roles := strings.Split(ret.(string), ",")
	roles = roles[:len(roles)-1]
	return roles, nil
}
func (c *Client) GetNodeInfo() (*p2p.NodeInfo, error) {
	node := p2p.NodeInfo{}
	err := c.rpcClient.Call(&node, "admin_nodeInfo")
	if err != nil {
		return nil, err
	}
	return &node, nil
}

type PeerNetwork struct {
	LocalAddress  string `json:"localAddress"`
	RemoteAddress string `json:"remoteAddress"`
}

type Peer struct {
	Id      string      `json:"id"`
	Enode   string      `json:"enode"`
	Name    string      `json:"name"`
	Network PeerNetwork `json:"network"`
}
type PeerList []Peer

func (c *Client) Peers() (PeerList, error) {
	var peers PeerList
	err := c.rpcClient.Call(&peers, "admin_peers")
	if err != nil {
		return peers, err
	}
	return peers, nil
}
func (list PeerList) getPeer(enode string) *Peer {
	node := strings.Split(enode, "@")[0]
	for _, peer := range list {
		peerNode := strings.Split(peer.Enode, "@")[0]
		if peerNode == node {
			return &peer
		}
	}
	return nil
}
func (c *Client) verifyStatus(nodes []*Node, selfNode string) ([]*Node, error) {
	peers, err := c.Peers()
	if err != nil {
		return nil, err
	}
	for index, node := range nodes {
		nodeId := strings.Split(node.EnodeId, "@")[0]
		if selfNode == nodeId {
			nodes[index].Status = NodeStatus[nodeNormal]
		} else {
			peer := peers.getPeer(node.EnodeId)
			if peer != nil {
				nodes[index].Status = NodeStatus[nodeNormal]
			} else {
				nodes[index].Status = NodeStatus[nodeAbnormal]
			}
		}
	}
	return nodes, nil
}
func (c *Client) minerStatus(status string) bool {
	if status == fmt.Sprintf("%v", NotMiner) {
		return false
	} else {
		return true
	}
}
func (c *Client) PeerCount() (int64, error) {
	var res interface{}
	err := c.rpcClient.Call(&res, "net_peerCount")
	if err != nil {
		return 0, err
	}
	afterN := strings.Split(res.(string), "0x")
	peerCount, err := strconv.ParseInt(afterN[1], 16, 64)
	return peerCount, nil
}

type SimpleNodeInfo struct {
	Encode     string `json:"enode"`
	Enr        string `json:"enr"`
	Id         string `json:"id"`
	Ip         string `json:"ip"`
	ListenAddr string `json:"listenAddr"`
	Name       string `json:"name"`
}

func (c *Client) NodeId() (*SimpleNodeInfo, error) {
	var info SimpleNodeInfo
	err := c.rpcClient.Call(&info, "admin_nodeInfo")
	if err != nil {
		return nil, err
	}
	return &info, nil
}
